#include "StompedReality/Core/TypeSystem/TypeInfo.h"
#include "StompedReality/Core/TypeSystem/TypeSystem.h"
#include <sstream>
#include <string.h>
namespace StompedReality
{
namespace type_system
{
TypeInfo* TypeInfo::addTypeField ( TypeField field )
{
  this->fields.push_back ( field );
  return this;
}

TypeInfo* TypeInfo::setWriter ( jsonWriteFunction writer )
{
  this->writer = writer;
  return this;
}

TypeInfo* TypeInfo::setReader ( jsonReaderFunction reader )
{
  this->reader = reader;
  return this;
}

bool TypeInfo::isPrimitive( )
{
  return this->fields.empty( );
}

void TypeInfo::write ( Json::Value& root, char* data )
{
  root["type"] = this->name;
  if ( this->isPrimitive( ) )
  {
    this->writer ( data, 0, root, "value" );
  }
  for ( Fields::iterator iter = this->fields.begin( );
        iter != this->fields.end( );
        ++iter )
  {
    if ( ( *iter ).list )
    {
      this->writeList ( root, data, *iter );
    }
    else
    {
      TypeInfo* typeInfo = TypeSystem::GetTypeByID ( ( *iter ).type );
      if ( !typeInfo->isPrimitive( ) )
      {
        Json::Value newNode;
        typeInfo->write ( newNode, data + ( *iter ).offset );
        root[ ( *iter ).name] = newNode;
      }
      else
      {
        typeInfo->writer ( data, ( *iter ).offset, root, ( *iter ).name );
      }
    }
  }
}

void TypeInfo::read ( Json::Value& root, char* data )
{
  if ( this->fields.empty( ) )
    return;
  else
  {
    for ( Fields::iterator iter = this->fields.begin( );
          iter != this->fields.end( );
          ++iter )
    {
      if ( ( *iter ).list )
      {
        this->readList ( root, data, *iter );
      }
      else
      {
        TypeInfo* typeInfo = TypeSystem::GetTypeByID ( ( *iter ).type );
        if ( !typeInfo->isPrimitive( ) )
        {
          typeInfo->read ( root[ ( *iter ).name], data + ( *iter ).offset );
        }
        else
        {
          typeInfo->reader ( data, ( *iter ).offset, root, ( *iter ).name );
        }
      }
    }
  }
}

std::string TypeInfo::to_s( )
{
  std::stringstream stream;
  stream << "Type: " << name << ", size : " << size << std::endl;
  for ( Fields::iterator iter = this->fields.begin( );
        iter != this->fields.end( );
        ++iter )
  {
    stream << " " << ( *iter ).to_s( ) << std::endl;
  }
  return stream.str( );
}

void TypeInfo::writeList ( Json::Value& root, char* data, const TypeField field )
{
  TypeInfo* typeInfo = TypeSystem::GetTypeByID ( field.type );
  char* c = ( char* ) ( ( std::vector<char>* )( data + field.offset ) )->data( );
  int s = ( ( ( std::vector<char>* )( data + field.offset ) )->size( ) / typeInfo->size );
  Json::Value list;
  for ( int i = 0; i < s; ++i )
  {
    Json::Value x;
    typeInfo->write ( x, c + typeInfo->size * i );
    if ( typeInfo->isPrimitive( ) )
    {
      list.append ( x["value"] );
    }
    else
    {
      list.append ( x );
    }
  }
  root[ field.name] = list;
}

void TypeInfo::readList ( Json::Value& root, char* data, const TypeField field )
{
  TypeInfo* typeInfo = TypeSystem::GetTypeByID ( field.type );
  std::vector<char>* vTemp = ( std::vector<char>* )( data + field.offset );
  vTemp->resize( typeInfo->size * root[field.name].size( ), 0 );
  for ( unsigned int i = 0; i < root[field.name].size( ); ++i )
  {
    if ( typeInfo->isPrimitive( ) )
    {
      Json::Value help;
      help["a"] = root[field.name][i];
      typeInfo->reader ( (char*)&(*vTemp->begin( )) + i * typeInfo->size, 0, help, "a" );
    }
    else
    {
      typeInfo->read ( root[field.name][i],  (char*)&(*vTemp->begin( )) + i * typeInfo->size );
    }
  }
}

TypeField TypeInfo::findField( const char* name )
{
  for (Fields::iterator iter = this->fields.begin( ); iter != this->fields.end( ); ++iter)
  {
    if (strcmp ( (*iter).name, name ) == 0 )
      return *iter;
  }
}

}
}
